﻿#include <stdio.h>
#include <unistd.h>
#include <stdint.h>
#include <sys/types.h>
#include <signal.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>

#include <libmedia/play/fb_video_player.h>
#include <libmedia/media_previewer.h>
#include <libmedia/read/isp_video_reader.h>
#include <libhardware2/camera_pixel_format.h>
#include <libmedia/media_alloter.h>
#include <libmedia/rotate/hw_video_rotater.h>

#include <libhardware2/v4l2_camera.h>

#include <libutils2/boot_time.h>

#include <scale/mixer_scale.h>
#include "debug/debug.h"
#include "media_ipc_msg.h"
#include "play_avi.h"
#include "photo_jpeg.h"
#include "media_message.h"
#include "media_soft_rotater.h"
#include "record_avi.h"
#include "media.h"

static struct media media = {
	.media_param = {
		.resolution_type = CAMERA_RES_NONE,
		.media_dir = "/",
	},
	.fb_param = {
		.fb_device = "/dev/fb0",
	},
   .previewer = NULL,
   .camera.camera_online = true,
};

struct scale_step video_scale_step[4] = {

	{
		.width = DST_HIGH_RESOLUTION_WIDTH,
		.height = DST_HIGH_RESOLUTION_HEIGHT,
	},
	{
		.width = HIGH_RESOLUTION_SCALE_1_WIDTH,
		.height = HIGH_RESOLUTION_SCALE_1_HEIGHT,
	},
	{
		.width = HIGH_RESOLUTION_SCALE_2_WIDTH,
		.height = HIGH_RESOLUTION_SCALE_2_HEIGHT,
	},
	{
		.width = HIGH_RESOLUTION_SCALE_3_WIDTH,
		.height = HIGH_RESOLUTION_SCALE_3_HEIGHT,
	},
};

char reader_state_str[][30] = {
	[READER_STATE_NONE] = "READER_STATE_NONE",
	[READER_STATE_STOP] = "READER_STATE_STOP",
	[READER_STATE_PAUSE] = "READER_STATE_PAUSE",
	[READER_STATE_START] = "READER_STATE_START",
	[READER_STATE_CONTINUE] = "READER_STATE_CONTINUE",
	[READER_STATE_THUMBNAIL] = "READER_STATE_THUMBNAIL",
	[READER_STATE_SELECT_FILE] = "READER_STATE_SELECT_FILE",
};

static int media_frame_alloc(void);
static void media_frame_free(void);
static int open_video_decode(struct reader_proc* reader);

static void init_video_params(void)
{
	memset(&media.camera.jpeg_decoder_param, 0, sizeof(media.camera.jpeg_decoder_param));
    turbo_jpeg_video_decoder_init_param(&media.camera.jpeg_decoder_param);
	pthread_mutex_init(&media.camera.mutex, NULL);
}

static void media_init_device_param(void)
{
	fb_video_player_init_param(&media.fb_param);

	media.previewer_param.video_player_param = &media.fb_param.param;

	init_video_params();

	return;
}

static void media_uninit(void)
{
	media.media_mode_proc.mode_state.value = 0;

	pthread_spin_destroy(&media.media_mode_proc.spin_lock);
	pthread_rwlock_destroy(&media.record_proc.rw_lock);
	pthread_rwlock_destroy(&media.scale_proc.rw_lock);
	pthread_rwlock_destroy(&media.rotater_proc.rw_lock);
	pthread_rwlock_destroy(&media.reader_proc.rw_lock);
	pthread_mutex_destroy(&media.camera.mutex);
}

static void media_init(void)
{
	media.media_mode_proc.mode_state.value = 0;

	pthread_spin_init(&media.media_mode_proc.spin_lock, 0);
	pthread_rwlock_init(&media.record_proc.rw_lock, 0);
	pthread_rwlock_init(&media.scale_proc.rw_lock, 0);
	pthread_rwlock_init(&media.rotater_proc.rw_lock, 0);
	pthread_rwlock_init(&media.reader_proc.rw_lock, 0);
	media_init_device_param();
}

struct message_queue* get_action_message_queue(enum video_action action)
{
	struct message_queue* queue = NULL;

    switch (action)
    {
		case VIDEO_ACTION_ROTATER:
			queue = media.rotater_proc.queue;
			break;
		case VIDEO_ACTION_SCALE:
		case VIDEO_ACTION_MIRROR:
		case VIDEO_ACTION_WHITEBLACK:
			queue = media.scale_proc.queue;
			break;
		case VIDEO_ACTION_PHOTO:
		case VIDEO_ACTION_RECORD:
			queue = media.record_proc.queue;
			break;
		case VIDEO_ACTION_PLAY:
			queue = media.reader_proc.queue;
			break;
		default:
			break;
	}

	return queue;
}

static int media_device_open(void)
{
	int ret = 0;

	media.record_proc.queue = video_message_queue_open();
	if (media.record_proc.queue == NULL)
	{
		print_error("record video frame queue open failed.");
		return -1;
	}

	media.rotater_proc.queue = video_message_queue_open();
	if (media.rotater_proc.queue == NULL)
	{
		print_error("rotater video frame queue open failed.");
		goto rotater_frame_queue_open_faild;
	}

	media.scale_proc.queue = video_message_queue_open();
	if (media.scale_proc.queue == NULL)
	{
		print_error("rotater video frame queue open failed.");
		goto scale_frame_queue_open_faild;
	}

	media.reader_proc.queue = video_message_queue_open();
	if (media.reader_proc.queue == NULL)
	{
		print_error("rotater video frame queue open failed.");
		goto reader_queue_open_faild;
	}

	media.camera.jpeg_decoder = video_decoder_open(&media.camera.jpeg_decoder_param.param);
	if(media.camera.jpeg_decoder == NULL)
		goto reader_proc_queue_failed;
	

	return 0;

reader_proc_queue_failed:
	video_message_queue_close(media.reader_proc.queue);

reader_queue_open_faild:
	video_message_queue_close(media.scale_proc.queue);

scale_frame_queue_open_faild:
	video_message_queue_close(media.rotater_proc.queue);

rotater_frame_queue_open_faild:
	video_message_queue_close(media.record_proc.queue);

	return -1;
}

static void media_frame_free(void)
{  
    video_frame_put(media.media_frame.crop_frame);
    video_frame_put(media.media_frame.mixer_scale_frame);
}   

static int media_frame_alloc(void)
{
	media.media_frame.crop_frame = video_frame_alloc();
	if (media.media_frame.crop_frame == NULL)
	{
		print_error("crop frame alloc faild.");
		return -1;
	}

	media.media_frame.crop_frame->put_frame = NULL;   
	video_frame_get(media.media_frame.crop_frame);

	media.media_frame.mixer_scale_frame = \
		media_alloter_alloc_video_frame(NULL, media.media_param.width, media.media_param.height, VIDEO_bgra, 0);
	if (media.media_frame.mixer_scale_frame == NULL)
	{
		print_error("mixer frame alloc faild.");
		goto mixer_scale_frame_failed;
	}    

	return 0;

mixer_scale_frame_failed:
	video_frame_put(media.media_frame.crop_frame);

	return -1;
}

static void media_close(void)
{

	if(media.rotater_proc.queue != NULL)
		video_message_queue_close(media.rotater_proc.queue);

	if (media.record_proc.queue != NULL)
		video_message_queue_close(media.record_proc.queue);

	if (media.scale_proc.queue != NULL)
		video_message_queue_close(media.scale_proc.queue);

	if (media.reader_proc.queue != NULL)
		video_message_queue_close(media.reader_proc.queue);

}

void timer_delay_usec(uint64_t timeout_usecs)
{
	struct timeval tp;

	tp.tv_sec = timeout_usecs / 1000000;
	tp.tv_usec = timeout_usecs % 1000000;

	select(0, NULL, NULL, NULL, &tp);

	return ;
}

void set_reader_pthread_quit(bool state)
{
    pthread_rwlock_wrlock(&media.reader_proc.rw_lock);
    media.reader_proc.thread_quit = state;
    pthread_rwlock_unlock(&media.reader_proc.rw_lock);
}

bool get_reader_pthread_quit(void)
{
    pthread_rwlock_rdlock(&media.reader_proc.rw_lock);
    bool state = media.reader_proc.thread_quit;
    pthread_rwlock_unlock(&media.reader_proc.rw_lock);

	return state;
}

void set_reader_state(enum reader_state state)
{
	pthread_rwlock_rdlock(&media.reader_proc.rw_lock);
	print_debug("set reader state to %s.", reader_state_str[state]);
	
	media.reader_proc.state = state;
	media.reader_proc.start_time = 0;
	
	if(state == READER_STATE_STOP || state == READER_STATE_SELECT_FILE)
	{
		demuxing_seek_backward(media.reader_proc.demuxing, 0);
		media.reader_proc.frame_count = 0;
		media.reader_proc.cur_duration = 0;
		media.reader_proc.frame_count = 0;
		media.reader_proc.start_time = 0;
		media.reader_proc.frame_played = 0;
	}
	else if( state == READER_STATE_START || state == READER_STATE_PAUSE )
	{
		media.reader_proc.frame_count = 0;
		media.reader_proc.cur_duration = 0;
		media.reader_proc.frame_count = 0;
		media.reader_proc.start_time = 0;
		
		if( state == READER_STATE_START )
			media.reader_proc.frame_played = 0;
		
	}

	pthread_rwlock_unlock(&media.reader_proc.rw_lock);
}

enum reader_state get_reader_state(void)
{
	enum reader_state state;

	pthread_rwlock_rdlock(&media.reader_proc.rw_lock);
	state = media.reader_proc.state;
	pthread_rwlock_unlock(&media.reader_proc.rw_lock);

	return state;
}

int do_play_video(enum reader_state state)
{
	struct reader_proc* reader = &media.reader_proc;
	assert(reader);
	#include <libavcodec/avcodec.h>

	struct media_packet *media_pkt = NULL;
	struct video_frame* frame = NULL;
	struct video_frame* nv12_frame = NULL;
	uint64_t consume_time = reader->frame_count * reader->pts_time_usec + reader->start_time; 
	uint64_t duration = 0;
	uint64_t play_interval = 0;
	int ret = -1;
	int64_t dts = 0;
	AVPacket * avpkt = NULL;

	media_pkt = media_play_read_pkt();
	if (media_pkt == NULL)
	{
		print_warning("play %s to the end. trun to stop.", reader->file_name);
		ack_replay_duration(0);
		set_reader_state(READER_STATE_STOP);
		return ret ;
	}

	avpkt = media_pkt->pdata;
	dts = avpkt->dts;
	ret = video_decoder_send_pkt(reader->video_decoder, media_pkt);
	if (ret < 0) {
		print_error("video decoder send media_pkt err.");
		media_packet_put(media_pkt);
		return 0;
	}

	while (ret >= 0)
	{
			
		if (frame != NULL)
		{
			video_frame_put(frame);
			frame = NULL;
			
			consume_time = reader->frame_count * reader->pts_time_usec + reader->start_time;
		}
		
		ret = video_decoder_get_frame(reader->video_decoder, &frame);
		if (ret == -MEDIA_EAGAIN || ret == -MEDIA_EOF)
		{
			break;
		}
		else if (ret < 0)
		{
			print_error("decoder get frame err, ret = %d.\n", ret);
			break;
		}

		nv12_frame = media_alloter_alloc_video_frame(NULL, frame->width, frame->height, VIDEO_nv12, 0);
		assert(nv12_frame);
		video_frame_copy(frame, nv12_frame);

		reader->cur_time = boot_time_usecs();

		if(state == READER_STATE_START){
			reader->start_time = reader->cur_time;
			reader->frame_count = 0;
			print_debug("!!!!!!!!!!!!start media_pkt.is_key frame %d dts %d",media_pkt->is_key_frame,dts);

		}else if (state == READER_STATE_CONTINUE)
		{
			if(consume_time > reader->cur_time)
			{
				play_interval =  consume_time - reader->cur_time;
		
				timer_delay_usec(play_interval);
			}
		}
		
		if( state == READER_STATE_STOP || state == READER_STATE_THUMBNAIL)
		{
			reader->frame_count = 0;
			reader->cur_duration = 0;
			ack_replay_duration(0);
		}
		else
		{
			reader->frame_count++;
			reader->frame_played++;
			if ((reader->frame_played * reader->pts_time_usec / 1000000) != reader->cur_duration)
			{
				reader->cur_duration = reader->frame_played * reader->pts_time_usec / 1000000;
				ack_replay_duration(reader->cur_duration);
			}
		}


		print_debug("media_pkt.is_key frame %ddts %d",media_pkt->is_key_frame,dts);

		media_frame_message_send(get_action_message_queue(VIDEO_ACTION_RECORD), nv12_frame, 0);
	}
	 
	if(frame != NULL)
		video_frame_put(frame);

	media_packet_put(media_pkt);

	return 0;
}

void close_video_decode(struct reader_proc* reader)
{
	pthread_rwlock_wrlock(&reader->rw_lock);

	if (reader->video_decoder != NULL)
	{
		video_decoder_close(reader->video_decoder);
		reader->video_decoder = NULL;
	}

	pthread_rwlock_unlock(&reader->rw_lock);
}

int open_video_decode(struct reader_proc* reader)
{
	int ret = -1;

	pthread_rwlock_wrlock(&reader->rw_lock);
	
	if (reader->video_decoder != NULL)
	{
		print_warning("video decoder already opened.");
		pthread_rwlock_unlock(&reader->rw_lock);
		return 0;
	}

	assert(reader->demuxing);

	media.reader_proc.decoder_param.config.width = DST_HIGH_RESOLUTION_WIDTH,
    media.reader_proc.decoder_param.config.height = DST_HIGH_RESOLUTION_HEIGHT,
    media.reader_proc.decoder_param.config.output_fmt = V4L2_PIX_FMT_NV12,
    media.reader_proc.decoder_param.config.video_path = "/dev/video2",

	ret = demuxing_get_video_param(reader->demuxing, &reader->decoder_param.param);
	if (ret) {
		print_error("get_video_param err.");
		pthread_rwlock_unlock(&reader->rw_lock);

		return ret;
	}
	
	hw_h264_video_decoder_init_param(&media.reader_proc.decoder_param);
	reader->video_decoder = video_decoder_open(&media.reader_proc.decoder_param.param);

	if (reader->video_decoder == NULL)
	{
		print_error("h264 video_decoder open failed.");
		pthread_rwlock_unlock(&reader->rw_lock);

		return -1;
	}

	pthread_rwlock_unlock(&reader->rw_lock);

	return 0;
}

void close_video_demux()
{
	media_play_close();
}

int open_video_demux(struct video_cmd* cmd)
{
	struct reader_proc* reader = &media.reader_proc;
	if (cmd->size < MAX_RECORD_FILE_NAME_LENGHT)
	{
		memcpy(reader->file_name, cmd->data, cmd->size);
		reader->file_name[cmd->size] = 0;
	}

	reader->demuxing = media_play_open(media.media_param.media_dir, reader->file_name);
	if (reader->demuxing == NULL)
	{
	   goto exit;
	}

	reader->total_duration = demuxing_get_duration(reader->demuxing);
	demuxing_get_video_param(reader->demuxing, &reader->decoder_param.param);

	reader->pts_time_usec = reader->decoder_param.param.fps_den;
	reader->pts_time_usec *= 1000000;
	reader->pts_time_usec /= reader->decoder_param.param.fps_num;

	print_debug(" reader->decoder_param.param.fps_num  %d   reader->decoder_param.param.fps_den %d",  reader->decoder_param.param.fps_num,  reader->decoder_param.param.fps_den);
	print_debug("%s/%s total duration:%lld pts_time_usec %llu", media.media_param.media_dir, reader->file_name, reader->total_duration, reader->pts_time_usec);

	return 0;

exit:
	bzero(reader->file_name, sizeof(reader->file_name));

	return -1;
}

void close_replay_media(struct reader_proc* reader)
{
	close_video_decode(reader);
	close_video_demux();
}

int open_replay_media(struct reader_proc* reader, struct video_cmd* cmd)
{
	int ret = -1;

	ret = open_video_demux(cmd);
	if(ret  < 0)
	{
		print_error("open video demux");
		close_video_decode(reader);
		return ret;
	}

	ret = open_video_decode(reader);
	if(ret < 0 )
	{
		print_error("open video decode");
		return ret;
	}

	return 0;
}

int dispose_video_reader(struct video_cmd* cmd)
{
	int ret = -1;
	struct reader_proc* reader = &media.reader_proc;
	uint16_t duration = 0;

	if (cmd->action != VIDEO_ACTION_PLAY)
		return ret;

	if(cmd->action_cmd != VIDEO_ACTION_CMD_PLAYING || get_reader_state() != READER_STATE_START)
		print_debug("cmd: %d  state:%s", cmd->action_cmd, reader_state_str[get_reader_state()]);

	if (cmd->action_cmd == VIDEO_ACTION_SELECT_FILE)
	{
		close_replay_media(reader);
		if (strcmp(&cmd->data[cmd->size - 4], ".avi") == 0 || strcmp(&cmd->data[cmd->size - 4], ".AVI") == 0)
		{
			ret = open_replay_media(reader, cmd);            
			if (ret == 0)
			{
				 duration = reader->total_duration / (1000 * 1000);
				 reader->cur_duration = 0;
				 set_reader_state(READER_STATE_SELECT_FILE);
				 do_play_video(READER_STATE_THUMBNAIL);
			}
		}

		ack_get_file_proc(ret, (uint8_t*)&duration, 2);
	}
	else if(cmd->action_cmd	== VIDEO_ACTION_CMD_AB_PLAY)
	{
		int32_t a_time = 0;
		memcpy(&a_time,cmd->data, 4);
		
		print_debug("set AB play a %d\n", a_time);
		// demuxing_seek_backward(reader->demuxing, 0);
		demuxing_seek_forward(reader->demuxing, a_time * 1000 * 1000);
		reader->frame_count = (a_time * 1000000.0 )/ reader->pts_time_usec;
		reader->frame_played = reader->frame_count;
		do_play_video(READER_STATE_START);
		ack_video_ab_play(ret);
	}
	else if (cmd->action_cmd == VIDEO_ACTION_CMD_START)
	{
		if (get_reader_state() == READER_STATE_STOP)
		{
			set_reader_state(READER_STATE_PAUSE);
		}

		if (get_reader_state() == READER_STATE_SELECT_FILE || get_reader_state() == READER_STATE_PAUSE)
		{
			set_reader_state(READER_STATE_START);
			
			do_play_video(READER_STATE_START);
			ret = media_send_action_cmd(VIDEO_ACTION_PLAY, VIDEO_ACTION_CMD_PLAYING, NULL, 0);
		}
		else
		{
			set_reader_state(READER_STATE_STOP);
			print_error("Can't start play, select file first.");
			ret = -1;
		}

		ack_video_repaly(ret,MEDIA_REPLAY_START);
	}
	else if (cmd->action_cmd == VIDEO_ACTION_CMD_STOP)
	{
		if (get_reader_state() == READER_STATE_START || get_reader_state() == READER_STATE_PAUSE)
		{
			do_play_video(VIDEO_ACTION_CMD_STOP);
		}
		set_reader_state(READER_STATE_STOP);
		ack_video_repaly(0, MEDIA_REPLAY_STOP);

		duration = reader->total_duration / (1000 * 1000);
		ack_get_file_proc(ret, (uint8_t*)&duration, 2);

	}
	else if (cmd->action_cmd == VIDEO_ACTION_CMD_PAUSE)
	{
		set_reader_state(READER_STATE_PAUSE);
		ack_video_repaly(0, MEDIA_REPLAY_PAUSE);
	}
	else if (cmd->action_cmd == VIDEO_ACTION_CMD_PLAYING && get_reader_state() == READER_STATE_START)
	{
		ret = do_play_video(READER_STATE_CONTINUE);
		if (ret < 0)
		{
			set_reader_state(READER_STATE_STOP);	
			ack_video_repaly(0, MEDIA_REPLAY_STOP);
			demuxing_seek_backward(reader->demuxing, 0);
			do_play_video(READER_STATE_STOP);
		}
		else
		{
			media_send_action_cmd(VIDEO_ACTION_PLAY, VIDEO_ACTION_CMD_PLAYING, NULL, 0);
		}
	}

	return 0;
}

//------------------------------------------------------------------------------------------
void dispose_reader_thread_msg(struct media* media, struct message_context* ctx)
{
	struct video_frame* overlayer_frame = NULL;
	struct video_cmd* cmd = NULL;
	int ret = -1;

    if (ctx->type == MESSAGE_TYPE_CMD)
    {
        cmd = ctx->data.cmd;

		if (cmd->action == VIDEO_ACTION_PLAY)
		{
			ret = dispose_video_reader(cmd);
		}

        video_cmd_put(cmd);
    }
    else 
    {
        print_warning("unsupport message cmd type, put conte .");
        video_message_context_data_put(ctx);
    }
}

void* pthread_media_reader(void* media)
{
	struct timespec timespec_start, timespec_end;
	uint64_t time_used_nsec;

	struct message_context* msg_ctx = NULL;
	struct media* p_media = (struct media*)media;
	assert(p_media);

	struct reader_proc* reader = &p_media->reader_proc;
	struct message_queue* queue = p_media->reader_proc.queue;

	reader->file_select = false;
	bzero(reader->file_name, sizeof(reader->file_name));
	
	while (get_reader_pthread_quit() == false || video_message_queue_size(queue) != 0)
	{
		msg_ctx = video_message_queue_receive(queue);
		assert(msg_ctx);

		if (msg_ctx->type != MESSAGE_TYPE_VIDEO_FRAME && get_reader_pthread_quit() == true)
		{
			video_message_context_put_all(msg_ctx);
			continue;
		}

		clock_gettime(CLOCK_REALTIME, &timespec_start);

		dispose_reader_thread_msg(p_media, msg_ctx);

		video_message_context_put(msg_ctx);

		clock_gettime(CLOCK_REALTIME, &timespec_end);

		time_used_nsec = (timespec_end.tv_sec * 1000000000 + timespec_end.tv_nsec) - (timespec_start.tv_sec * 1000000000 + timespec_start.tv_nsec);

		//print_info("record and play :%lld mse mesage queue size:%d.", time_used_nsec / 1000000, video_message_queue_size(queue));

	}

	close_replay_media(reader);
	
	return NULL;
}

bool is_doing_preview()
{
	pthread_spin_lock(&media.media_mode_proc.spin_lock);
	bool state = !!media.media_mode_proc.mode_state.state.media_preview_start;
	pthread_spin_unlock(&media.media_mode_proc.spin_lock);

	return state;
}

bool get_record_state()
{
	pthread_rwlock_rdlock(&media.record_proc.rw_lock);
	bool state = !!media.record_proc.record_state.data;
	pthread_rwlock_unlock(&media.record_proc.rw_lock);

	return state;
}

bool get_record_video_state(void)
{
	pthread_rwlock_rdlock(&media.record_proc.rw_lock);
	bool state = !!media.record_proc.record_state.state.start_record;
	pthread_rwlock_unlock(&media.record_proc.rw_lock);

	return state;
}

bool get_record_photo_state(void)
{
	pthread_rwlock_rdlock(&media.record_proc.rw_lock);
	bool state = !!media.record_proc.record_state.state.start_photo;
	pthread_rwlock_unlock(&media.record_proc.rw_lock);

	return state;
}

int set_record_photo_state(bool state, char * filename)
{
	pthread_rwlock_wrlock(&media.record_proc.rw_lock);
	if (state == true)
	{
		if (strlen(filename) >= MAX_RECORD_FILE_NAME_LENGHT)
		{
			pthread_rwlock_unlock(&media.record_proc.rw_lock);
			return -1;
		}
		memcpy(media.record_proc.name[RECORD_TYPE_PHOTO], filename, strlen(filename));
	}
	media.record_proc.record_state.state.start_photo = state;

	pthread_rwlock_unlock(&media.record_proc.rw_lock);

	return 0;
}

int set_record_video_state(bool state, char* filename)
{
	int ret = 0;

	pthread_rwlock_wrlock(&media.record_proc.rw_lock);
	media.record_proc.name[RECORD_TYPE_VIDEO][0] = '0';
	if (state == true)
	{
		if (strlen(filename) >= MAX_RECORD_FILE_NAME_LENGHT)
		{
			pthread_rwlock_unlock(&media.record_proc.rw_lock);
			return -1;
		}
	   
		memcpy(media.record_proc.name[RECORD_TYPE_VIDEO], filename, strlen(filename));
		media.record_proc.start_time = boot_time_secs();
		media.record_proc.current_time = 0;
	}
	else 
	{
		media.record_proc.start_time = 0;
	}

	media.record_proc.record_state.state.start_record = state;
	pthread_rwlock_unlock(&media.record_proc.rw_lock);
	return 0;
}

int get_record_file_name(enum record_type type, char* name)
{
	pthread_rwlock_rdlock(&media.record_proc.rw_lock);
		
	if (strlen(media.record_proc.name[type]) == 0 || strlen(media.record_proc.name[type]) >= MAX_RECORD_FILE_NAME_LENGHT)
	{
		media.record_proc.name[type][0] = '0';
		return -1;
	}
	memcpy(name, media.record_proc.name[type], strlen(media.record_proc.name[type]));
	pthread_rwlock_unlock(&media.record_proc.rw_lock);

	return 0;
}

int do_record_player_frame_message(struct media* media, struct video_frame* frame)
{
    struct media_packet *packet = NULL;

    int ret = -1;
    char file_name[MAX_RECORD_FILE_NAME_LENGHT + 1] = {0};
    
    if(media->previewer != NULL)
	{
        media_previewer_display_video_frame(media->previewer, frame, 0);
	}

	if (is_media_previewer_start() == true)
    {
		if (get_record_video_state() == true)
		{
			ret = do_media_record(frame);
			if (ret < 0)
			{
				set_record_video_state(false, NULL);
				media_record_stop();
				ack_video_record(MEDIA_CMD_RECORDING, ret);

			}

		}

        if (get_record_photo_state() == true)
        {
            if (get_record_file_name(RECORD_TYPE_PHOTO, file_name) == 0)
            {
                ret = media_photo_frame(media->media_param.media_dir, file_name, frame);
                ack_video_photo(ret);

            }
            set_record_photo_state(false, NULL);
        }
    }

	return 0;
}

void set_record_pthread_quit(bool state)
{
	pthread_rwlock_wrlock(&media.rotater_proc.rw_lock);
	media.record_proc.thread_quit = state;
	pthread_rwlock_unlock(&media.rotater_proc.rw_lock);
}

bool get_record_pthread_quit()
{
	pthread_rwlock_rdlock(&media.rotater_proc.rw_lock);
	bool state = media.record_proc.thread_quit;
	pthread_rwlock_unlock(&media.rotater_proc.rw_lock);

	return state;
}

void dispose_record_thread_msg(struct media* media, struct message_context* ctx)
{
	struct video_frame* overlayer_frame = NULL;
	struct video_cmd* cmd = NULL;
	int ret = -1;

	if (ctx->type == MESSAGE_TYPE_CMD)
	{
		char file_name[MAX_RECORD_FILE_NAME_LENGHT] = {0};

		cmd = ctx->data.cmd;

		if (cmd->action == VIDEO_ACTION_PHOTO)
		{
			if (cmd->action_cmd == VIDEO_ACTION_CMD_START && strlen(cmd->data) == cmd->size)
			{
				ret = set_record_photo_state(true, cmd->data);
			}

			if (ret < 0)
				ack_video_photo(ret);
		}
		else if (cmd->action == VIDEO_ACTION_RECORD)
		{
			if (cmd->action_cmd == VIDEO_ACTION_CMD_START && strlen(cmd->data) == cmd->size)
			{
				if (get_record_video_state() == false)
				{
					ret = set_record_video_state(true, cmd->data);
					if (ret == 0 && get_record_file_name(RECORD_TYPE_VIDEO, file_name) == 0)
					{
						ret = media_record_start(media->media_param.media_dir, file_name, media->media_param.width, media->media_param.height);

						print_debug("start video record %s/%s  width:%d heigh:%d.", media->media_param.media_dir, file_name, \
							media->media_param.width, media->media_param.height);
					}
				}

                ack_video_record(MEDIA_CMD_RECORD_START, ret);
            }
            else if (cmd->action_cmd == VIDEO_ACTION_CMD_STOP)
            {
                set_record_video_state(false, NULL);
                media_record_stop();
                ack_video_record(MEDIA_CMD_RECORD_STOP, 0);
            }
		}
		video_cmd_put(cmd);
    }
    else if (ctx->type == MESSAGE_TYPE_VIDEO_FRAME)
    {
        overlayer_frame = ctx->data.frame;
        do_record_player_frame_message(media, overlayer_frame);
        video_frame_put(overlayer_frame);
    }
    else
    {
        video_message_context_data_put(ctx);
    }
}

void * media_pthread_record_player_handler(void *media)
{
	struct timespec timespec_start, timespec_end;
	uint64_t time_used_nsec;
	struct message_context* msg_ctx = NULL;
	struct media* p_media = (struct media*)media;

	assert(p_media);

	struct record_proc *p_record_proc = &p_media->record_proc;
	struct message_queue* queue = p_media->record_proc.queue;

	assert(queue);

	while( get_record_pthread_quit() == false || video_message_queue_size(queue) != 0)
	{     
		clock_gettime(CLOCK_REALTIME, &timespec_start);

		msg_ctx = video_message_queue_receive(queue);
		assert(msg_ctx);

        if (msg_ctx->type != MESSAGE_TYPE_VIDEO_FRAME && get_record_pthread_quit() == true)
        {
            video_message_context_put_all(msg_ctx);
            continue;
        }
	
        dispose_record_thread_msg(p_media, msg_ctx);

		video_message_context_put(msg_ctx);

		clock_gettime(CLOCK_REALTIME, &timespec_end);

		time_used_nsec = (timespec_end.tv_sec * 1000000000 + timespec_end.tv_nsec) - (timespec_start.tv_sec * 1000000000 + timespec_start.tv_nsec);

	    // print_info("record and play :%lld mse mesage queue size:%d.", time_used_nsec / 1000000, video_message_queue_size(queue));
	}

	media_record_stop();

	return NULL;
}

void set_video_rotater_angle(enum video_rotate_angle angle)
{
	pthread_rwlock_wrlock(&media.rotater_proc.rw_lock);
	media.rotater_proc.rotate_angle = angle;
	pthread_rwlock_unlock(&media.rotater_proc.rw_lock);

}

enum video_rotate_angle get_video_rotater_angle(void)
{
	enum video_rotate_angle angle;
	pthread_rwlock_rdlock(&media.rotater_proc.rw_lock);
	angle = media.rotater_proc.rotate_angle;
	pthread_rwlock_unlock(&media.rotater_proc.rw_lock);

	return angle;
}

void set_video_whiteblack(bool whiteblack)
{
	pthread_rwlock_wrlock(&media.rotater_proc.rw_lock);
	media.scale_proc.enable_whiteblack = whiteblack;
	pthread_rwlock_unlock(&media.rotater_proc.rw_lock);
}

bool get_video_whiteblack(void)
{
	enum video_rotate_angle angle;
	pthread_rwlock_rdlock(&media.rotater_proc.rw_lock);
	angle = media.scale_proc.enable_whiteblack;
	pthread_rwlock_unlock(&media.rotater_proc.rw_lock);

	return angle;
}

void set_rotater_thread_quit(bool state)
{
	pthread_rwlock_wrlock(&media.rotater_proc.rw_lock);
	media.rotater_proc.thread_quit = state;
	pthread_rwlock_unlock(&media.rotater_proc.rw_lock);

}

bool get_rotater_thread_quit()
{
	pthread_rwlock_rdlock(&media.rotater_proc.rw_lock);
	bool state = media.rotater_proc.thread_quit;
	pthread_rwlock_unlock(&media.rotater_proc.rw_lock);

	return state;
}

void set_video_scale(enum video_scale_up scale_factor)
{
	pthread_rwlock_wrlock(&media.scale_proc.rw_lock);
	media.scale_proc.scale_step = scale_factor;
	print_debug("set video sclale:%d.", scale_factor);
	pthread_rwlock_unlock(&media.scale_proc.rw_lock);
}

enum video_scale_up get_video_scale(void)
{
	pthread_rwlock_rdlock(&media.scale_proc.rw_lock);
	enum video_scale_up scale_factor = media.scale_proc.scale_step;
	pthread_rwlock_unlock(&media.scale_proc.rw_lock);

	return scale_factor;
}

void set_scale_thread_quit(bool state)
{
	pthread_rwlock_wrlock(&media.scale_proc.rw_lock);
	media.scale_proc.thread_quit = state;
	pthread_rwlock_unlock(&media.scale_proc.rw_lock);
}

bool get_scale_thread_quit()
{
	pthread_rwlock_rdlock(&media.scale_proc.rw_lock);
	bool state = media.scale_proc.thread_quit;
	pthread_rwlock_unlock(&media.scale_proc.rw_lock);

	return state;
}

static int vidoe_input_search_dev_name(char *dev_name)
{
    DIR *dir;
    struct dirent *entry;
    char dev_path[300];
    int ret = -1;
    dir = opendir("/sys/class/video4linux");
    if (dir == NULL) {
        perror("opendir");
        return ret;
    }
    while ((entry = readdir(dir)) != NULL) {
        if (entry->d_type == DT_LNK && strncmp(entry->d_name, "video", 5) == 0) {
			
			if(entry->d_name[5] == '0' ||entry->d_name[5] == '1' || entry->d_name[5] == '2' )
				continue;

            snprintf(dev_path, sizeof(dev_path), "/dev/%s", entry->d_name);
            print_debug("Video device: %s,path exist = %d\n", dev_path,access(dev_path,F_OK));
            strcpy(dev_name,dev_path);
            ret = 0;
            break;
        }
    }
    closedir(dir);    
    return ret;
}

static void uvc_camera_reset(void)
{
	print_debug("uvc_camera_reset");
	if(media.camera.camera_opened == true)
	{
		v4l2_camera_close(media.camera.camera);
		media.camera.camera = NULL;
		media.camera.camera_opened = false;
		media.camera.camera_error = false;
	}

	system("/usr/bin/usb_otg_host.sh");
	system("cmd_gpio set_func PB25 output0");
	usleep(200 * 1000);
	system("cmd_gpio set_func PB25 output1");
}

static void video_uvc_check(void)
{
	bool  camera_online = false;
	int ret = -1;
    struct v4l2_camera_format format;
	static int uvc_outline_count = 0;

	memset(&format, 0 , sizeof(format));
	pthread_mutex_lock(&media.camera.mutex);
	
	if(access(media.camera.name, F_OK) == 0)
		camera_online = true;
	else
	{
		vidoe_input_search_dev_name(media.camera.name);
		if(access(media.camera.name, F_OK) == 0)
			camera_online = true;
	}
	
	if(camera_online == true && media.camera.camera_error == false)
		uvc_outline_count = 0;
	else
	{
		uvc_outline_count ++;
		if(uvc_outline_count >= 10)
		{
			uvc_camera_reset();
			uvc_outline_count = 0;
		}
	}

	if(media.camera.camera_opened && (camera_online == false || media.camera.camera_error))
	{
		goto camera_close;
	}

	if(camera_online && media.camera.camera_opened == false)
	{
		media.camera.camera = v4l2_camera_open(media.camera.name);
		if(media.camera.camera == NULL)
		{
			media.camera.camera_error = true;
			goto camera_close;
		}

    	v4l2_camera_detect_format(media.camera.camera, &format);
	    v4l2_camera_dump_format(&format);
		
		ret = v4l2_camera_create_buffer(media.camera.camera, &format, 3);
		if(ret != 0)
		{
			media.camera.camera_error = true;
			goto camera_close;
		}
	
		ret = v4l2_camera_stream_on(media.camera.camera);
    	if (ret)
		{
			media.camera.camera_error = true;
			goto camera_close;
		}

		media.camera.camera_error = false;
		media.camera.camera_opened = true;
	}

	if(media.camera.camera_online != camera_online)
	{
		media.camera.camera_online = camera_online;
		if(camera_online)
			media_send_action_cmd(VIDEO_ACTION_RECORD, VIDEO_ACTION_CMD_STOP, 0, 0);

		ack_camera(camera_online);
	}
	
	pthread_mutex_unlock(&media.camera.mutex);
	return ;

camera_close:
	uvc_camera_reset();

	pthread_mutex_unlock(&media.camera.mutex);
}

int16_t cal_gsensor_angle(short x, short y, short z)
{
	int16_t roll;
	int16_t max = 0, min = 0, aveg = 0;
	int i = 1;

	roll = (uint16_t)(atan2((float)(x), z) * 180 / 3.14159);

	if(roll < 0)
		roll =  360 + roll;
	
	if(media.gsensor.pos >= 10)
		media.gsensor.pos = 0;
	
	media.gsensor.angle_num[media.gsensor.pos] = roll;
	media.gsensor.pos++;
	
	if(media.gsensor.angle_cnt < 10)
		media.gsensor.angle_cnt++;

	max = media.gsensor.angle_num[0];
	min = media.gsensor.angle_num[0];
	aveg = media.gsensor.angle_num[0];

	if(media.gsensor.angle_cnt <= 2)
		return roll;

	for(i = 1; i < media.gsensor.angle_cnt; i++)
	{
		if(media.gsensor.angle_num[i] > max)
			max = media.gsensor.angle_num[i];
		
		if(media.gsensor.angle_num[i] < min)
			min = media.gsensor.angle_num[i];

		aveg += media.gsensor.angle_num[i];

	}

	aveg -= (min + max);

	aveg /= (media.gsensor.angle_cnt - 2);
	
	return aveg;
}

int video_reader_uvc_frame(struct video_frame** frame, int16_t *angle)
{
	int ret = -1;
	uint8_t gsensor_data[7] = {0};
	struct v4l2_camera_buffer camera_buf;
    struct media_packet pkt = {
        .data = 0,
        .size = 0,
        .type = VIDEO_pkt_mjpeg,
    };

	video_uvc_check();
	if(media.camera.camera == NULL || media.camera.camera_opened == false)
		return ret;
	
	memset(&camera_buf, 0, sizeof(camera_buf));

  	ret = v4l2_camera_dequeue_buffer(media.camera.camera, &camera_buf, 1000);
    if (ret)
	{
		fprintf(stderr, "v4l2_camera_dequeue_buffer error\n");
        goto camera_error;
	}
		
	pkt.data = camera_buf.data[0];
	pkt.size = camera_buf.size[0];

	memcpy(gsensor_data, pkt.data + 28, 7);
	media.gsensor.x = ((int16_t) (gsensor_data[2] << 8 | gsensor_data[1])) >> 4;
	media.gsensor.y = ((int16_t) (gsensor_data[4] << 8 | gsensor_data[3])) >> 4;
	media.gsensor.z = ((int16_t) (gsensor_data[6] << 8 | gsensor_data[5])) >> 4;

 	ret = video_decoder_send_pkt(media.camera.jpeg_decoder, &pkt);
	if (ret) {
		fprintf(stderr, "video_decoder_get_frame\n");
		goto decoder_error;
	}

	ret = video_decoder_get_frame(media.camera.jpeg_decoder, frame);
	if (ret) {
		fprintf(stderr, "failed to get frame\n");
		goto decoder_error;
	}
	
	v4l2_camera_queue_buffer(media.camera.camera, &camera_buf);
	int sensor_angle = cal_gsensor_angle(media.gsensor.x, media.gsensor.y, media.gsensor.z);
	// print_debug("sensor angle %d",*angle);
	if(sensor_angle < 180)
		sensor_angle = 180 - sensor_angle;
	else 
		sensor_angle = 540 - sensor_angle;
	// print_debug("rotater angle %d",*angle);
	*angle = sensor_angle;
	return ret;

decoder_error:
	v4l2_camera_queue_buffer(media.camera.camera, &camera_buf);
camera_error:
	pthread_mutex_lock(&media.camera.mutex);
	media.camera.camera_error = true;
	pthread_mutex_unlock(&media.camera.mutex);

	return ret;
}

void yuv422p_to_yuv420sp(struct video_frame *src, struct video_frame *dst)
{
	int i = 0, j = 0;

	uint8_t *u_frame = src->data[1];
	uint8_t *v_frame = src->data[2];

	uint8_t *uv_frame = dst->data[1];

	memcpy(dst->data[0], src->data[0], src->size[0]);
	dst->size[0] = src->size[0];
	dst->total_size = src->size[0];
	dst->linesize[0] = src->linesize[0];
	dst->size[1] = 0;

	for(i  = 0 ; i < src->height/2; i++)
	{
		for(j = 0; j < src->width/2; j++)
		{
			uv_frame[j*2] = u_frame[j];
			uv_frame[j*2+1] = v_frame[j];
		}

		uv_frame += src->width;
		dst->size[1] += src->width;
		u_frame += src->width;
		v_frame += src->width;
	}

	dst->total_size += dst->size[1];
	dst->linesize[1] = video_format_line_size(VIDEO_nv12, 1, src->width, 1);
}

struct video_frame* dispost_rotater_thread_msg(struct media* media, struct message_context* ctx)
{
	struct video_frame* rotater_frame = NULL;
	struct video_frame* scale_nv12_frame = NULL;
	struct video_frame* overlayer_frame = NULL;
	struct video_cmd* cmd = NULL;

	struct timespec timespec_start, timespec_end;
	uint64_t time_used_nsec;

	struct rotater_proc *p_rotater_proc = &media->rotater_proc;
	int ret = -1;

	assert(p_rotater_proc);

	int video_width = media->media_param.width;
	int video_height = media->media_param.height;

    if (ctx->type == MESSAGE_TYPE_VIDEO_FRAME)
    {
        scale_nv12_frame = ctx->data.frame;
		// if(get_video_rotater_angle() == rotate_0 || ctx->angle == 0)
		if(get_video_rotater_angle() == rotate_0)
			overlayer_frame = scale_nv12_frame;
		else 
		{
			clock_gettime(CLOCK_REALTIME, &timespec_start);
			ret = media_soft_rotater(scale_nv12_frame, &rotater_frame, ctx->angle);
			if (ret == 0)
			{
				video_frame_put(scale_nv12_frame);

				overlayer_frame = rotater_frame;
			}
			else
				overlayer_frame = NULL;
		}
			clock_gettime(CLOCK_REALTIME, &timespec_end);
			        time_used_nsec = (timespec_end.tv_sec * 1000000000 + timespec_end.tv_nsec) - (timespec_start.tv_sec * 1000000000 + timespec_start.tv_nsec);
	    //    print_info("rotater:%lld msec .", time_used_nsec / 1000000);

    }
    else if (ctx->type == MESSAGE_TYPE_CMD)
    {
        cmd = ctx->data.cmd;
        // 旋转
        if (cmd->action == VIDEO_ACTION_ROTATER)
        {
            set_video_rotater_angle(cmd->data[0]);
            ack_video_rotater(0);
		}


        video_cmd_put(cmd);
    }
    else
    {
        print_warning("unsupport message cmd type, put conte .");
        video_message_context_data_put(ctx);
    }

	return overlayer_frame;
}

void* media_pthread_rotater_handler(void *media)
{
	struct video_frame* rotater_frame = NULL;
	pthread_attr_t pth_attr;
	struct sched_param pth_param;
	enum message_type type;
	struct media* p_media = (struct media*)media;
	assert(p_media);

	struct message_context* msg_ctx = NULL;

	struct message_queue* record_queue = p_media->record_proc.queue;
	struct message_queue* rotater_queue = p_media->rotater_proc.queue;
	
	int ret = 0;

	while (get_rotater_thread_quit() == false || video_message_queue_size(rotater_queue) != 0)
	{
		msg_ctx = video_message_queue_receive(rotater_queue);
		assert(msg_ctx);
		
		//线程退出时，清空消息队列
		if (msg_ctx->type != MESSAGE_TYPE_VIDEO_FRAME && get_rotater_thread_quit() == true)
		{
			video_message_context_put_all(msg_ctx);
			continue;
		}

		rotater_frame = dispost_rotater_thread_msg(p_media, msg_ctx);

		if (msg_ctx->type == MESSAGE_TYPE_VIDEO_FRAME && rotater_frame != NULL)
		{
			if( video_message_queue_size (record_queue) <= MAX_RECORD_MESSAGE_QUEUE_COUNT)
				media_frame_message_send(record_queue, rotater_frame, 0);
			else 
			{
				video_frame_put(rotater_frame);
			}
		}
		
		video_message_context_put(msg_ctx);

	}   

	return NULL;
}

void * media_pthread_scale_handler(void* media)
{    
	struct video_frame* frame = NULL;
	struct video_frame* scale_nv12_frame = NULL;
	struct message_context* msg_ctx = NULL;
	struct video_cmd* cmd = NULL;

	int scale_width;
	int scale_height;
	enum video_scale_up scale_step;
	struct media* p_media = (struct media*)media;
	assert(p_media);
	enum camera_resolution_type res_type = p_media->media_param.resolution_type;
	struct video_frame* mixer_scale_frame = p_media->media_frame.mixer_scale_frame;
	struct video_frame* crop_frame = p_media->media_frame.crop_frame;
	struct scale_proc *p_scale_proc = &p_media->scale_proc;
	struct message_queue* rotater_queue = get_action_message_queue(VIDEO_ACTION_ROTATER);
	struct message_queue* scale_queue = get_action_message_queue(VIDEO_ACTION_SCALE);

	struct mixer_scale_out_param scale_param = {
		.cfg.format = fb_fmt_ARGB8888,
	};

	int video_width = p_media->media_param.width;
	int video_height = p_media->media_param.height;

	int ret = 0;
	int current_scale_step = 0;

	ret = media_frame_alloc();
	if (ret < 0)
	{
		print_error("media freame alloc failed.");
		kill(getpid(), 16);
	}

	mixer_scale_frame = p_media->media_frame.mixer_scale_frame;
	crop_frame = p_media->media_frame.crop_frame;

	assert(p_media->previewer);
	assert(mixer_scale_frame);
	assert(crop_frame);
	assert(p_scale_proc);
	assert(rotater_queue);
	assert(scale_queue);

	scale_param.cfg.xres = p_media->media_param.width;
	scale_param.cfg.yres = p_media->media_param.height;
	scale_param.cfg.dst_mem = (void*)mixer_scale_frame->phys_data[0];
	scale_param.cfg.dst_mem_virtual = mixer_scale_frame->data[0];              

   ret =  mixer_scale_out_init_param(&scale_param);
   if (ret < 0)
   {
	   print_error("mixer scale out init param faild.");
	   return NULL;
   }

   struct timespec timespec_start, timespec_end;   
   uint64_t time_used_nsec;
   int policy = 0;
	struct video_frame * nv12_convert_frame = NULL;
	nv12_convert_frame = media_alloter_alloc_video_frame(NULL, video_width, video_height, VIDEO_nv12, 0);

	while (get_scale_thread_quit() == false || message_queue_size(scale_queue) > 0)
	{    
		if( ret == 0 )
			clock_gettime(CLOCK_REALTIME, &timespec_start);

		if(message_queue_size(scale_queue) > 0)
		{
			msg_ctx =  video_message_queue_receive(scale_queue);

			if (get_scale_thread_quit() == true)
			{
				video_message_context_put_all(msg_ctx);
				continue;
			}

			if (msg_ctx->type == MESSAGE_TYPE_CMD)
			{
				cmd = msg_ctx->data.cmd;
				if (cmd->action == VIDEO_ACTION_SCALE)
				{
					set_video_scale(cmd->data[0]);
					ack_video_scale(0);
				}else  if (cmd->action == VIDEO_ACTION_WHITEBLACK)
				{
					set_video_whiteblack(cmd->data[0]);
					ack_video_whiteblack(0);
				}else  if (cmd->action == VIDEO_ACTION_MIRROR)
				{
					v4l2_camera_flip(p_media->camera.camera, cmd->data[0]);
					ack_video_mirror(0);
				}
				video_cmd_put(cmd);
			}

			video_message_context_put(msg_ctx);
		}

		clock_gettime(CLOCK_REALTIME, &timespec_start);

		int16_t angle = 0;
		ret = video_reader_uvc_frame(&frame, &angle);
		if (ret < 0)
		{
			if(p_media->camera.camera_error || p_media->camera.camera_online == false)
			   usleep(100 * 1000);
			else
				print_error("read frame error.");
			continue;;
		}

		if (ret == 1) {
		   usleep(10 * 1000);
			continue;
		}

		scale_step = get_video_scale();
		scale_width = video_scale_step[scale_step % MAX_VIDEO_SCALE_UP_STEP].width;
		scale_height = video_scale_step[scale_step % MAX_VIDEO_SCALE_UP_STEP].height;
	
		yuv422p_to_yuv420sp(frame, nv12_convert_frame);

		video_frame_crop(nv12_convert_frame, crop_frame, (frame->width - scale_width) / 2, (frame->height - scale_height) / 2, scale_width, scale_height);
		video_frame_get(crop_frame);

		mixer_scale_update_in_param(crop_frame);
		ret = mixer_scale_work_out_one_frame();
		if (ret < 0)
		{
			print_error("mixer_scale_work_out_one_frame failed.");
			video_frame_put(frame);
			continue;
		}

		scale_nv12_frame = media_alloter_alloc_video_frame(NULL, video_width, video_height, VIDEO_nv12, 0);
		assert(scale_nv12_frame);
		video_frame_copy(mixer_scale_frame, scale_nv12_frame);

		if(get_video_whiteblack() == true)
		{
			memset(scale_nv12_frame->data[1], 128, scale_nv12_frame->size[1]);
		}

		video_frame_put(frame);
        
        if(message_queue_size(rotater_queue) <= MAX_ROTATE_MESSAGE_QUEUE_COUNT)
            media_frame_message_send(rotater_queue, scale_nv12_frame, angle);
        else                
        video_frame_put(scale_nv12_frame);

        clock_gettime(CLOCK_REALTIME, &timespec_end);
        time_used_nsec = (timespec_end.tv_sec * 1000000000 + timespec_end.tv_nsec) - (timespec_start.tv_sec * 1000000000 + timespec_start.tv_nsec);
        // print_info("getframe time: %lld msc message size:%d .", time_used_nsec / 1000000, video_message_queue_size(scale_queue));
    }

	video_frame_put(nv12_convert_frame);
	mixer_scale_out_uninit();
	media_frame_free();

	return NULL;
}

int start_reader_thread(void)
{
	int ret = -1;

	set_reader_pthread_quit(false);
	
	ret = pthread_create(&media.reader_proc.thread_id, NULL, pthread_media_reader, (void*)&media);
	return ret;
}

void stop_reader_thread(void)
{
	set_reader_pthread_quit(true);
	media_notify_message_send(media.reader_proc.queue);

	if (media.reader_proc.thread_id != 0)
		pthread_join(media.reader_proc.thread_id, NULL);
}


int start_scale_thread(void)
{
	int ret = -1;
	
	pthread_attr_t attr;
	struct sched_param sched;
	struct sched_param param;

	set_scale_thread_quit(false);
	ret = pthread_create(&media.scale_proc.thread_id, NULL, media_pthread_scale_handler, (void*)&media);

	return ret;
}

void stop_scale_thread(void)
{
	set_scale_thread_quit(true);
	if (media.scale_proc.thread_id != 0)
	{
		pthread_join(media.scale_proc.thread_id, NULL);
		media.scale_proc.thread_id = 0;
	}
		
}

int start_rotater_thread(void)
{
	int ret = -1;
	set_rotater_thread_quit(false);
	ret = pthread_create(&media.rotater_proc.thread_id, NULL, media_pthread_rotater_handler, (void*)&media);

	return ret;
}

void stop_rotater_thread(void)
{
	set_rotater_thread_quit(true);
	media_notify_message_send(media.rotater_proc.queue);
	if (media.rotater_proc.thread_id != 0)
	{
		pthread_join(media.rotater_proc.thread_id, NULL);
		media.rotater_proc.thread_id = 0;
	}
		
}

int start_record_player_thread(void)
{
	int ret = -1;
	set_record_pthread_quit(false);

    ret = pthread_create(&media.record_proc.thread_id, NULL, media_pthread_record_player_handler, (void*)&media);

    return ret;
}

void stop_record_player_thread(void)
{
	set_record_pthread_quit(true);
	media_notify_message_send(media.record_proc.queue);

	if (media.record_proc.thread_id != 0)
	{
		pthread_join(media.record_proc.thread_id, NULL);
		media.record_proc.thread_id = 0;
	}
}

void media_set_video_resolution(int width, int height)
{
	media.media_param.width = width;
	media.media_param.height = height;
}

int media_reader_start(void)
{
	int ret = -1;

	print_function_entry();
	pthread_spin_lock(&media.media_mode_proc.spin_lock);

	if (media.media_mode_proc.mode_state.state.media_preview_start == true)
	{
		print_warning("preview mode need stop first.");
		goto failed;
	}


	if (media.media_mode_proc.mode_state.state.media_play_start == true)
	{
		print_warning("play mode already started.");
		goto failed;
	}

	if (media.previewer == NULL)
	{
		print_error("media previewer start failed.");
		goto failed;
	}

	if (media.record_proc.thread_id == 0)
	{
		ret = start_record_player_thread();
		if (ret < 0)
		{
			print_error("creatthread_id failed %s", strerror(errno));
			goto failed;
		}
	}

	ret = start_reader_thread();
	if (ret < 0)
	{
		print_error("creat reader thread failed %s", strerror(errno));
		goto failed;
	}

	media.reader_proc.file_select = false;
	memset(media.reader_proc.file_name, 0, sizeof(media.reader_proc.file_name));
	media.media_mode_proc.mode_state.state.media_play_start = true;

	pthread_spin_unlock(&media.media_mode_proc.spin_lock);
	print_function_success();

	return 0;

failed:
	pthread_spin_unlock(&media.media_mode_proc.spin_lock);

	return -1;
}

struct media_mode_proc get_media_mode(void)
{
	struct media_mode_proc mode;
	pthread_spin_lock(&media.media_mode_proc.spin_lock);
	mode = media.media_mode_proc;
	pthread_spin_unlock(&media.media_mode_proc.spin_lock);

	return mode;
}

void media_reader_stop(void)
{
	pthread_spin_lock(&media.media_mode_proc.spin_lock);

	if (media.media_mode_proc.mode_state.state.media_play_start == false)
	{
		print_warning("play already stoped.");
		pthread_spin_unlock(&media.media_mode_proc.spin_lock);
		return;
	}

	stop_reader_thread();
	close_replay_media(&media.reader_proc);

	media.media_mode_proc.mode_state.state.media_play_start = false;

	pthread_spin_unlock(&media.media_mode_proc.spin_lock);
}

bool is_media_previewer_start(void)
{
    bool state = false;
    pthread_spin_lock(&media.media_mode_proc.spin_lock);
    state = media.media_mode_proc.mode_state.state.media_preview_start;
    pthread_spin_unlock(&media.media_mode_proc.spin_lock);

    return state;
}

int media_previewer_start(void)
{
	int ret = -1;
	
	pthread_spin_lock(&media.media_mode_proc.spin_lock);

	if (media.media_mode_proc.mode_state.state.media_preview_start == true)
	{
		print_warning("previewer already started.");
		pthread_spin_unlock(&media.media_mode_proc.spin_lock);

		return 0;
	}

	memset(&media.gsensor, 0, sizeof(media.gsensor));

    if (media.encoder.video_encoder == NULL)
    {
        hw_h264_encoder_init_default_param(&media.encoder.encoder_param, media.media_param.width, media.media_param.height);

        media.encoder.video_encoder = video_encoder_open(&media.encoder.encoder_param.param);
        if (media.encoder.video_encoder == NULL)
        {
            print_error("video encoder open failed.");
            goto video_encoder_open_failed;
        }
    }
    
	if (media.media_mode_proc.mode_state.state.media_play_start == true)
	{
		print_warning("play mode need stop first.");
		goto previewer_start_failed;
	}

	if (media.record_proc.thread_id == 0)
	{
		ret = start_record_player_thread();
		if (ret < 0)
		{
			print_error("creatthread_id failed %s", strerror(errno));
			goto record_player_thread_failed;
		}
	}

	ret = start_rotater_thread();
	if (ret < 0)
	{
		print_error("creatthread_id failed %s", strerror(errno));
		goto record_rotater_thread_failed;
	}

	ret = start_scale_thread();
	if (ret < 0)
	{
		print_error("creatthread_id failed %s", strerror(errno));
		goto record_scale_thread_failed;
	}

	media.media_mode_proc.mode_state.state.media_preview_start = true;
	pthread_spin_unlock(&media.media_mode_proc.spin_lock);

	return ret;

record_scale_thread_failed:
	stop_rotater_thread();

record_rotater_thread_failed:
record_player_thread_failed:
previewer_start_failed:
     video_encoder_close(media.encoder.video_encoder);

video_encoder_open_failed:
    pthread_spin_unlock(&media.media_mode_proc.spin_lock);

	return ret;
}

void media_previewer_stop(void)
{
	pthread_spin_lock(&media.media_mode_proc.spin_lock);

	if (media.media_mode_proc.mode_state.state.media_preview_start == false)
	{
		print_warning("previewer already stoped.");
		pthread_spin_unlock(&media.media_mode_proc.spin_lock);

		return ;
	}
	print_info("previewer stop");

	stop_scale_thread();
	stop_rotater_thread();

    if (media.encoder.video_encoder != NULL)
    {
        video_encoder_close(media.encoder.video_encoder);
        media.encoder.video_encoder = NULL;
    }

	media.media_mode_proc.mode_state.state.media_preview_start = false;

	pthread_spin_unlock(&media.media_mode_proc.spin_lock);

	print_info("previewer stop");
}


void media_stop(void)
{
	media_previewer_stop();
	media_reader_stop();

    stop_record_player_thread();

    if (media.previewer != NULL)
    {
        media_previewer_close(media.previewer);
        media.previewer = NULL;
    }

    media_close();
    media_uninit();
}

int media_start(void)
{
	int ret = -1;

	media.media_param.width = DST_HIGH_RESOLUTION_WIDTH;
	media.media_param.height = DST_HIGH_RESOLUTION_HEIGHT;

	media_init();

    ret = media_device_open();
    if (ret < 0)
    {
        print_error("media open failed.");
        return ret;
    }

    if (media.previewer == NULL)
    {
        media.previewer = media_previewer_open(&media.previewer_param);
        if (media.previewer == NULL)
        {
            print_error("media previewer start failed.");
            return ret;
        }
    }

	ret = media_previewer_start();

	return ret;

}